---
title: "Canadian Election Misinformation Project"
author: "Mathieu Lavigne"
date: "2026-04-07"
output: html_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## Set working directory and load packages

```{r}
# Set working directory
setwd(dirname(rstudioapi::getActiveDocumentContext()$path))

# Load packages (remove # and run first line if pacman package not already installed)
# install.packages("pacman")
pacman::p_load(tidyverse, survey, scales, modelsummary, marginaleffects, jtools, estimatr, ggpubr, Hmisc, texreg, patchwork, xtable, psych, weights, kableExtra)
```

## Create folders for tables and figures

```{r}
if (!dir.exists("Figures")) {
  dir.create("Figures")
  message("Created 'Figures' folder.")
} else {
  message("'Figures' folder already exists.")
}

if (!dir.exists("Tables")) {
  dir.create("Tables")
  message("Created 'Tables' folder.")
} else {
  message("'Tables' folder already exists.")
}
```

## Functions

```{r}
summarize_weighted <- function(data, dv, ivs, weight_var, group_vars = NULL, var_label = NULL, filter_expr = NULL) {
  
  # Optionally filter the data
  if (!is.null(filter_expr)) {
    data <- data %>% filter(!!rlang::parse_expr(filter_expr))
  }
  
  # Select variables
  vars_to_select <- c(ivs, dv, weight_var)
  data <- data %>% dplyr::select(all_of(vars_to_select)) %>% na.omit()
  
  # Group by IVs and any additional grouping
  group_vars <- if (is.null(group_vars)) ivs else c(ivs, group_vars)
  
  data %>%
    group_by(across(all_of(group_vars))) %>%
    dplyr::summarize(
      value = weighted.mean(.data[[dv]], w = .data[[weight_var]]),
      sd = sqrt(wtd.var(.data[[dv]], weights = .data[[weight_var]])),
      n = n(),
      .groups = "drop"
    ) %>%
    mutate(
      var = var_label,
      se = sd / sqrt(n),
      lower = value - 1.96 * se,
      upper = value + 1.96 * se
    )
}
```

## Load data

```{r}
# Load data
cemp <- read_csv("./cemp.csv")
```

## Recode variables

```{r}
# Trust in election administration
cemp <- cemp %>%
  mutate(
    count_accurat_5point = ifelse(trust_accuracy_2_post %in% 1:5, trust_accuracy_2_post, NA),
    count_accurat_5point_pre = ifelse(fair_election_pre %in% 1:5, fair_election_pre, NA),
    trust_results_5point = ifelse(trust_accuracy_3_post %in% 1:5, trust_accuracy_3_post, NA),
    fair_election_4point = recode(fair_election_post, `1`=4, `2`=3, `3`=2, `4`=1, .default = NA_real_)
    )

# Distrust mail-in ballots
cemp <- cemp %>%
  mutate(
    trust_mail_vote = recode(mail_vote_pre, `1` = 1, `2` = 2, `3` = 3,
                                `4` = 4, `5` = 5, .default = NA_real_),
    distrust_mail_vote = recode(mail_vote_pre, `1` = 1, `2` = 0.75, `3` = 0.5,
                                `4` = 0.25, `5` = 0, .default = NA_real_),
    distrust_mail_vote01 = recode(mail_vote_pre, `1` = 1, `2` = 1, `3` = 0,
                                  `4` = 0, `5` = 0, .default = NA_real_),
    trust_mail_vote_post = recode(mail_vote_post, `1` = 1, `2` = 2, `3` = 3,
                                `4` = 4, `5` = 5, .default = NA_real_),
    distrust_mail_vote_post = recode(mail_vote_post, `1` = 1, `2` = 0.75, `3` = 0.5,
                                `4` = 0.25, `5` = 0, .default = NA_real_),
    distrust_mail_vote01_post = recode(mail_vote_post, `1` = 1, `2` = 1, `3` = 0,
                                  `4` = 0, `5` = 0, .default = NA_real_)
    )

# Media habits
cemp <- cemp %>% 
  mutate(
    freq_news_pre = recode(freq_news_pre, `1` = 1, `2` = 0.75, `3` = 0.5, `4` = 0.25, `5` = 0),
    freq_socmed_pre = recode(freq_socmed_pre, `1` = 1, `2` = 0.8, `3` = 0.6, `4` = 0.4, `5` = 0.2, `6` = 0),
    rebel = ifelse(!is.na(media_list_16_pre), 1, 0),
    truenorth = ifelse(!is.na(media_list_22_pre), 1, 0),
    postmillennial = ifelse(!is.na(media_list_20_pre), 1, 0),
    right_news = ifelse(rebel == 1 | postmillennial == 1 | truenorth == 1, 1, 0)
  )

# Political orientations
cemp <- cemp %>% 
  mutate(
    feeling_lib01=party_rating_1_pre/100,
    vote_choice = case_when(
      yesvote_post == 1 ~ "Liberal",
      yesvote_post == 2 ~ "Conservative",
      yesvote_post == 3 ~ "New Democrat",
      yesvote_post == 4 ~ "Bloc Québécois",
      yesvote_post == 5 ~ "Green",
      yesvote_post == 6 ~ "People's Party"
    ),
    vote_choice = factor(vote_choice, 
                         levels=c("Liberal", "Conservative", "New Democrat", "Bloc Québécois",
                                  "Green", "People's Party")),
    ideol = ideol_1_pre/10,
    ideol3 = case_when(
      ideol_1_pre %in% c(0,1,2,3) ~ "Left",
      ideol_1_pre %in% c(4,5,6) ~ "Moderate",
      ideol_1_pre %in% c(7,8,9,10) ~ "Right"
    )
    )

# Control variables
cemp <- cemp %>% 
  mutate(
    interest = interest_1_pre/10,
    age_cat_pre = case_when(
    age_pre >= 18 & age_pre < 25 ~ 0,
    age_pre >= 25 & age_pre < 35 ~ 0.2,
    age_pre >= 35 & age_pre < 45 ~ 0.4,
    age_pre >= 45 & age_pre < 55 ~ 0.6,
    age_pre >= 55 & age_pre < 65 ~ 0.8,
    age_pre >= 65 ~ 1
  ),
  age18_24 = ifelse(age_pre >= 18 & age_pre < 25, 1, 0),
  age25_34 = ifelse(age_pre >= 25 & age_pre < 35, 1, 0),
  age35_44 = ifelse(age_pre >= 35 & age_pre < 45, 1, 0),
  age45_54 = ifelse(age_pre >= 45 & age_pre < 55, 1, 0),
  age55_64 = ifelse(age_pre >= 55 & age_pre < 65, 1, 0),
  age65_ = ifelse(age_pre >= 65, 1, 0),
  college = ifelse(educ_cat3_pre == 0.5, 1, 0),
  university = ifelse(educ_cat3_pre == 1, 1, 0),
  age_cat3_pre = case_when(
    age_pre >= 18 & age_pre < 35 ~ 0,
    age_pre >= 35 & age_pre < 55 ~ 0.5,
    age_pre >= 55 ~ 1
  ),
  region = case_when(
    province1_pre == "Atlantic" ~ "Atlantic",
    province1_pre %in% c("Alberta", "Saskatchewan", "Manitoba") ~ "Prairies",
    province1_pre == "British Columbia" ~ "British Columbia",
    province1_pre == "Quebec" ~ "Quebec",
    province1_pre == "Ontario" ~ "Ontario",
    TRUE ~ NA_character_
  ),
  region4 = case_when(
    province1_pre == "Atlantic" ~ "Atlantic",
    province1_pre %in% c("Alberta", "Saskatchewan", "Manitoba", "British Columbia") ~ "West",
    province1_pre == "Quebec" ~ "Quebec",
    province1_pre == "Ontario" ~ "Ontario",
    TRUE ~ NA_character_
  ))

# Survey designs
cemp_nona <- filter(cemp, !is.na(wt_region_pre))
cemp_design <- svydesign(ids=~1, data=cemp_nona, weights = cemp_nona$wt_region_pre)
cemp_post_nona <- filter(cemp, !is.na(wt_region_post))
cemp_post_design <- svydesign(ids=~1, data=cemp_post_nona, weights = cemp_post_nona$wt_region_post)
```

## Main regression models: Association between media consumption and perceptions of election administration

```{r}
# Define control variables (matching CEMP structure)
controls_vote <- "freq_news_pre + ideol + feeling_lib01 + interest + age_cat_pre + educ_cat3_pre + female_pre + region"

# Media habits models
lm_fair_election_socmed <- lm_robust(data = cemp, weights = wt_region_post,
  formula = formula(paste0("fair_election_4point ~ freq_socmed_pre + ", controls_vote)), se_type = "HC2")
lm_trust_results_socmed <- lm_robust(data = cemp, weights = wt_region_post,
  formula = formula(paste0("trust_results_5point ~ freq_socmed_pre + ", controls_vote)), se_type = "HC2")
lm_count_accurat_socmed <- lm_robust(data = cemp, weights = wt_region_post,
  formula = formula(paste0("count_accurat_5point ~ freq_socmed_pre + ", controls_vote)), se_type = "HC2")

# Adding use of right-wing media
lm_fair_election_right <- lm_robust(data = filter(cemp), weights = wt_region_post,
  formula = formula(paste0("fair_election_4point ~ freq_socmed_pre + right_news +", controls_vote)), se_type = "HC2")
lm_trust_results_right <- lm_robust(data = filter(cemp), weights = wt_region_post,
  formula = formula(paste0("trust_results_5point ~ freq_socmed_pre + right_news +", controls_vote)), se_type = "HC2")
lm_count_accurat_right <- lm_robust(data = filter(cemp), weights = wt_region_post,
  formula = formula(paste0("count_accurat_5point ~ freq_socmed_pre + right_news +", controls_vote)), se_type = "HC2")
```

## Figure 6: Association between media consumption and perceptions of election administration

```{r}
fig_6 <- bind_rows(
  broom::tidy(lm_fair_election_socmed) %>% mutate(outcome = "Fair election", model = "Social media"),
  broom::tidy(lm_fair_election_right) %>% mutate(outcome = "Fair election", model = "Right-wing media"),
  broom::tidy(lm_trust_results_socmed) %>% mutate(outcome = "Trust results", model = "Social media"),
  broom::tidy(lm_trust_results_right) %>% mutate(outcome = "Trust results", model = "Right-wing media"),
  broom::tidy(lm_count_accurat_socmed) %>% mutate(outcome = "Counted accurately", model = "Social media"),
  broom::tidy(lm_count_accurat_right) %>% mutate(outcome = "Counted accurately", model = "Right-wing media")
) %>% 
  
  filter(term %in% c("right_news", "freq_socmed_pre", "freq_news_pre", "ideol")) %>% 
  mutate(predictor = factor(
    term, 
    levels = c("ideol", "freq_news_pre", "freq_socmed_pre", "right_news"),
    labels = c("Ideology\n(0=Left, 1=Right)", 
               "All-source news\nconsumption", 
               "Social Media-source\nnews consumption", 
               "Right-Wing\nmedia consumption")),
    outcome = factor(
      outcome, 
      levels = c("Fair election", "Trust results", "Counted accurately")
    ))  %>% 
  ggplot(aes(x=estimate, y=predictor, color=model, shape = model)) +
  geom_errorbar(aes(xmin=estimate - 1.96*std.error, xmax=estimate + 1.96*std.error), 
                position=position_dodge(width=0.7), width=0) +
  geom_point(position=position_dodge(width=0.7), size=3, fill = "white") +
  geom_vline(xintercept=0, linetype="dashed", color="black") +
  facet_wrap(~outcome) +
  scale_shape_manual(values = c(21, 23)) + 
  scale_color_manual(values = RColorBrewer::brewer.pal(3, "Dark2")[c(1,2)]) +
  theme_minimal() +
  labs(title=NULL, color = NULL, shape = NULL,
       x="Coefficient estimate", y="Predictor") +
  theme(
    panel.border = element_rect(color = "black", fill = NA, linewidth = 0.5),
    strip.text = element_text(size=12, face="bold"),
    legend.position = "top",
    legend.key.width = unit(3, "cm"), 
    legend.key.size = unit(1, "cm"),
    legend.key.height = unit(1, "cm")
  )

# Save figure
ggsave(fig_6, file="./Figures/Figure_6_media_coef_plot.png", height=4.5, width=9, dpi=600, bg = "white")
```

## Table A4: Sample characteristics

```{r}
print(xtable(data.frame(Demographic = c("Gender", "",
                           "Age", "", "",
                           "Education", "", "",
                           "Region", "", "", ""),
           Category = c("Male", "Female",
                        "18-34", "25-54", "55+",
                        "High school or less", "College/CEGEP", "University",
                        "Atlantic", "Ontario", "Quebec",  "West"),
           `Unweighted Pre` = c(prop.table(table(cemp_nona[["female_pre"]])),
                                prop.table(table(cemp_nona[["age_cat3_pre"]])),
                                prop.table(table(cemp_nona[["educ_cat3_pre"]])),
                                prop.table(table(cemp_nona[["region4"]]))
           ),
           `Weighted Pre` = c(as.data.frame(svymean(~factor(female_pre), cemp_design, na.rm = T))$mean,
                              as.data.frame(svymean(~factor(age_cat3_pre), cemp_design, na.rm = T))$mean,
                              as.data.frame(svymean(~factor(educ_cat3_pre), cemp_design, na.rm = T))$mean,
                              as.data.frame(svymean(~factor(region4), cemp_design, na.rm = T))$mean),
           `Unweighted Post` = c(prop.table(table(cemp_post_nona[["female_pre"]])),
                                 prop.table(table(cemp_post_nona[["age_cat3_pre"]])),
                                 prop.table(table(cemp_post_nona[["educ_cat3_pre"]])),
                                 prop.table(table(cemp_post_nona[["region4"]]))),
           `Weighted Post` = c(as.data.frame(svymean(~factor(female_pre), cemp_post_design, na.rm = T))$mean,
                               as.data.frame(svymean(~factor(age_cat3_pre), cemp_post_design, na.rm = T))$mean,
                               as.data.frame(svymean(~factor(educ_cat3_pre), cemp_post_design, na.rm = T))$mean,
                               as.data.frame(svymean(~factor(region4), cemp_post_design, na.rm = T))$mean))),
           type = "html", file="./Tables/Table_A4_sample_cemp.html")
```

## Figure A1: Distribution of media use by ideology

```{r}
rightwing <- svyby(~factor(right_news), ~ideol3, cemp_post_design, svymean, na.rm = T) %>% 
  pivot_longer(-ideol3, names_to = "names", values_to = "Values") %>% 
  mutate(Exposure = case_when(
    grepl("1", names) ~ "Exposed \n \n",
    grepl("0", names) ~ "Not exposed \n \n"
  ),
  Stat = ifelse(grepl("se", names), "se", "mean")) %>% 
  select(-names) %>% 
  pivot_wider(names_from = "Stat", values_from = "Values") %>% 
  mutate(lower = mean - 1.96*se,
         upper = mean + 1.96*se, 
         variable = "Alternative right-wing media")

news <- svyby(~factor(freq_news_pre), ~ideol3, cemp_post_design, svymean, na.rm = T) %>% 
  pivot_longer(-ideol3, names_to = "names", values_to = "Values") %>% 
  mutate(Exposure = case_when(
    grepl("1", names) ~ "Every day",
    grepl("75", names) ~ "Almost every day",
    grepl("25", names) ~ "Once",
    grepl("5", names) ~ "A few times",
    grepl("0", names) ~ "Never"
  ),
  Stat = ifelse(grepl("se", names), "se", "mean")) %>% 
  select(-names) %>% 
  pivot_wider(names_from = "Stat", values_from = "Values") %>% 
  mutate(lower = mean - 1.96*se,
         upper = mean + 1.96*se, 
         variable = "General",
         Exposure = factor(Exposure, 
                           levels=c("Never", "Once", "A few times", "Almost every day", "Every day"),
                           labels=c("Never", "Once", "A\nfew\ntimes", "Almost\nevery\nday", "Every\nday")))

social <- svyby(~factor(freq_socmed_pre), ~ideol3, cemp_post_design, svymean, na.rm = T) %>% 
  pivot_longer(-ideol3, names_to = "names", values_to = "Values") %>% 
  mutate(Exposure = case_when(
    grepl("1", names) ~ "Several times a day",
    grepl("8", names) ~ "Every day",
    grepl("6", names) ~ "Almost every day",
    grepl("4", names) ~ "A few times",
    grepl("2", names) ~ "Once",
    grepl("0", names) ~ "Never"
  ),
  Stat = ifelse(grepl("se", names), "se", "mean")) %>% 
  select(-names) %>% 
  pivot_wider(names_from = "Stat", values_from = "Values") %>% 
  mutate(lower = mean - 1.96*se,
         upper = mean + 1.96*se, 
         variable = "Social media",
         Exposure = factor(Exposure, 
                           levels=c("Never", "Once", "A few times", "Almost every day",
                                    "Every day", "Several times a day"),
                           labels=c("Never", "Once", "A\nfew\ntimes", "Almost\nevery\nday",
                                    "Every\nday", "Several\ntimes\na day")
         )
  )

plot_news <- rbind(news) %>%
  ggplot(aes(x=Exposure, y=mean, ymin=lower, ymax=upper, col=ideol3, shape=ideol3))+
  geom_point(position=position_dodge(0.5), size=2)+
  geom_errorbar(width=0, position=position_dodge(0.5))+
    scale_color_manual(values = c("red4", "darkgrey", "blue4"))+
    scale_shape_manual(values = c(17,16,15))+
  scale_y_continuous(label=percent_format(accuracy=1), 
                     limits=c(0,0.5))+
  labs(color="",
       shape="",
       y="Percentage of respondents", 
       title="General")+
  theme_minimal()+
  theme(axis.title.x = element_blank(),
        plot.title=element_text(hjust=0.5, size=11))

plot_social <- rbind(social) %>% 
  ggplot(aes(x=Exposure, y=mean, ymin=lower, ymax=upper, col=ideol3, shape=ideol3))+
  geom_point(position=position_dodge(0.5), size=2)+
  geom_errorbar(width=0, position=position_dodge(0.5))+
    scale_color_manual(values = c("red4", "darkgrey", "blue4"))+
    scale_shape_manual(values = c(17,16,15))+
  scale_y_continuous(label=percent_format(accuracy=1),
                     limits=c(0,0.5))+
  labs(title="Social media")+
  theme_minimal()+
  theme(axis.title=element_blank(),
        axis.text.y=element_blank(),
        plot.title=element_text(hjust=0.5, size=11))

plot_rightwing <- rbind(rightwing) %>% 
    filter(Exposure=="Exposed \n \n") %>% 
  ggplot(aes(x=Exposure, y=mean, ymin=lower, ymax=upper, col=ideol3, shape=ideol3))+
  geom_point(position=position_dodge(0.5), size=2)+
  geom_errorbar(width=0, position=position_dodge(0.5))+
    scale_color_manual(values = c("red4", "darkgrey", "blue4"))+
  scale_shape_manual(values = c(17,16,15))+
  scale_y_continuous(label=percent_format(accuracy=1), 
                     limits=c(0,0.5))+
  labs(title="Right-wing media")+
  theme_minimal()+
  theme(axis.title=element_blank(),
        axis.text.y=element_blank(),
        plot.title=element_text(hjust=0.5, size=11))

fig_a1 <- ggarrange(plot_news, plot_social, plot_rightwing, nrow=1, widths=c(1,1,0.4), common.legend = TRUE)

ggsave(fig_a1, file="./Figures/Figure_A1_media_use.png", bg="white", height=4.5, width=9)
```

## Table B4: OLS table: Perceptions of election administration by media habits (social media)

```{r}
# Create additional row for Fixed Effects
new_rows_B4 <- tribble(~term, ~lm_fair_election_socmed, ~lm_trust_results_socmed, ~lm_count_accurat_socmed,
  "Region fixed effects", "Yes", "Yes", "Yes")
attr(new_rows_B4, 'position') <- c(10)

# Create table
modelsummary(
  list(
    "Fairness<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_fair_election_socmed,
    "Trust results<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_trust_results_socmed,
    "Votes counted accurately<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_count_accurat_socmed
  ),
  coef_map = c(
    "freq_socmed_pre" = "Freq. social media",
    "freq_news_pre" = "Freq. news",
    "ideol" = "Ideology (L-R)",
    "feeling_lib01" = "Liberal Party feeling",
    "interest" = "Political interest",
    "age_cat_pre" = "Age",
    "educ_cat3_pre" = "Education",
    "female_pre" = "Female",
    "(Intercept)" = "(Intercept)"
  ),
  estimate = "{estimate}&nbsp;&nbsp;{std.error}&nbsp;&nbsp;{p.value}",
  statistic = NULL,
  output = "./Tables/Table_B4.html",
  add_rows = new_rows_B4,
  gof_omit = "AIC|BIC|Std.Errors|RMSE",
  escape = FALSE    
)
```

## Table B5: OLS table: Perceptions of election administration by media habits (social + right-wing media)

```{r}
# Create additional row for Fixed Effects
new_rows_B5 <- tribble(~term, ~lm_fair_election_right, ~lm_trust_results_right, ~lm_count_accurat_right,
  "Region fixed effects", "Yes", "Yes", "Yes")
attr(new_rows_B5, 'position') <- c(11)

# Create table
modelsummary(
  list(
    "Fairness<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_fair_election_right,
    "Trust results<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_trust_results_right,
    "Votes counted accurately<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_count_accurat_right
  ),
  coef_map = c(
    "freq_socmed_pre" = "Freq. social media",
    "right_news" = "Right-wing media",
    "freq_news_pre" = "Freq. news",
    "ideol" = "Ideology (L-R)",
    "feeling_lib01" = "Liberal Party feeling",
    "interest" = "Political interest",
    "age_cat_pre" = "Age",
    "educ_cat3_pre" = "Education",
    "female_pre" = "Female",
    "(Intercept)" = "(Intercept)"
  ),
  estimate = "{estimate}&nbsp;&nbsp;{std.error}&nbsp;&nbsp;{p.value}",
  statistic = NULL,
  output = "./Tables/Table_B5.html",
  add_rows = new_rows_B5,
  gof_omit = "AIC|BIC|Std.Errors|RMSE",
  escape = FALSE    
)
```

## Figure C1: Distrust in mail voting by party and ideology

```{r}
# By vote choice
mailin_vote <- summarize_weighted(data = cemp, dv = "distrust_mail_vote01", ivs = c("vote_choice"),  weight_var = "wt_region_pre", var_label = "Low trust in mail-in ballots") %>% 
  mutate(
    vote_choice = case_when(
      vote_choice=="Liberal" ~ "\nLiberal",
      vote_choice=="Conservative" ~ "Conservative",
      vote_choice=="New Democrat" ~ "\nNew\nDemocrat",
      vote_choice=="Bloc Québécois" ~ "Bloc\nQuébécois",
      vote_choice=="Green" ~ "Green",
      vote_choice=="People's Party" ~ "People's\nParty"
    ),
    vote_choice=factor(vote_choice, levels=c("\nLiberal", "Conservative", "\nNew\nDemocrat", "Bloc\nQuébécois", "Green", "People's\nParty")))%>% 
  ggplot(aes(x=vote_choice, y=value, ymin=lower, ymax=upper, col=vote_choice, shape=vote_choice))+
  geom_point(size=2.5)+
  geom_errorbar(width=0, size=0.8)+
  scale_y_continuous(limits = c(-0.01, 0.76), breaks = c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7),
                     labels = percent_format(accuracy = 1))+
  scale_color_manual(values = c("red3","blue3", "orange", "cyan3", "green4", "purple"))+
  scale_shape_manual(values = c(16, 15, 17, 18, 20, 8))+
  labs(x="Vote choice",
       y="Weighted percentage with low trust in mail-in ballots",
       title="A) By vote choice")+
  guides(col="none", shape="none")+
  theme_minimal()+
  theme(legend.position="top",
        plot.title = element_text(size=12))

# By ideology
mailin_ideol <- summarize_weighted(data = cemp, dv = "distrust_mail_vote01", ivs = c("ideol_1_pre"),  weight_var = "wt_region_pre", var_label = "Low trust in mail-in ballots") %>%
  mutate(
    ideol3 = case_when(
      ideol_1_pre %in% c(0,1,2,3) ~ "Left",
      ideol_1_pre %in% c(4,5,6) ~ "Moderate", 
      ideol_1_pre %in% c(7,8,9,10) ~ "Right"),
    ideol_char=case_when(
      ideol_1_pre == 0 ~ "0\n\n",
      ideol_1_pre == 1 ~ "1\n\n",
      ideol_1_pre == 2 ~ "2\n\n",
      ideol_1_pre == 3 ~ "3\n\n",
      ideol_1_pre == 4 ~ "4\n\n",
      ideol_1_pre == 5 ~ "5\n\n",
      ideol_1_pre == 6 ~ "6\n\n",
      ideol_1_pre == 7 ~ "7\n\n",
      ideol_1_pre == 8 ~ "8\n\n",
      ideol_1_pre == 9 ~ "9\n\n",
      ideol_1_pre == 10 ~ "10\n\n",
    ),
    ideol_char=factor(ideol_char, levels=c("0\n\n","1\n\n","2\n\n","3\n\n","4\n\n","5\n\n",
                                           "6\n\n","7\n\n","8\n\n","9\n\n","10\n\n"))) %>% 
  ggplot(aes(x=ideol_char, y=value, ymin=lower, ymax=upper, col=factor(ideol3), shape=factor(ideol3)))+
  geom_point(size=2.5)+
  geom_errorbar(width=0, size=0.8)+
  scale_y_continuous(limits = c(-0.01, 0.76), breaks = c(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7),
                     labels = percent_format(accuracy = 1))+
  scale_color_manual(values = c("red4", "darkgrey", "blue4"))+
  scale_shape_manual(values = c(2,1,0))+
  labs(x="Left-right ideology (0-10)",
       y="Weighted percentage with low trust in mail-in ballots",
       title="B) By ideology")+
  guides(col="none", shape="none")+
  theme_minimal()+
  theme(legend.position="top",
        plot.title = element_text(size=12),
        axis.title.y=element_blank(),
        axis.text.y=element_blank())

fig_c1 <- ggarrange(mailin_vote, mailin_ideol, nrow=1, widths = c(1.2,1))

# Save figure
ggsave(fig_c1, file="./Figures/Figure_C1_mail_cemp.png", bg="white", height=4)
```

## Table C1: Association between distrust in mail-in ballots and perceptions of election administration

```{r}
# Define control variables
controls_mail <- "freq_news_pre + ideol + feeling_lib01 + interest + age_cat_pre + educ_cat3_pre + female_pre + region"

# Estimate models
lm_count_accurat_mail <- lm_robust(data = cemp, weights = wt_region_post,
   formula = formula(paste0("count_accurat_5point ~ distrust_mail_vote + ", controls_mail)), se_type = "HC2")
lm_trust_results_mail <- lm_robust(data = cemp, weights = wt_region_post,
   formula = formula(paste0("trust_results_5point ~ distrust_mail_vote + ", controls_mail)), se_type = "HC2")
lm_fair_election_mail <- lm_robust(data = cemp, weights = wt_region_post,
   formula = formula(paste0("fair_election_4point ~ distrust_mail_vote + ", controls_mail)), se_type = "HC2")

# Create table
# Create additional row for Fixed Effects
new_rows_C1 <- tribble(~term, ~lm_fair_election_mail, ~lm_trust_results_mail, ~lm_count_accurat_mail,
  "Region fixed effects", "Yes", "Yes", "Yes")
attr(new_rows_C1, 'position') <- c(10)

modelsummary(
  list(
    "Fairness<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_fair_election_mail,
    "Trust results<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_trust_results_mail,
    "Votes counted accurately<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_count_accurat_mail
  ),
  coef_map = c(
    "distrust_mail_vote" = "Distrust mail voting",
    "freq_news_pre" = "Freq. news",
    "ideol" = "Ideology (L-R)",
    "feeling_lib01" = "Liberal Party feeling",
    "interest" = "Political interest",
    "age_cat_pre" = "Age",
    "educ_cat3_pre" = "Education",
    "female_pre" = "Female",
    "(Intercept)" = "Intercept"
  ),
  estimate = "{estimate}&nbsp;&nbsp;{std.error}&nbsp;&nbsp;{p.value}",
  statistic = NULL,
  output = "./Tables/Table_C1.html",
  add_rows = new_rows_C1,
  gof_omit = "AIC|BIC|Std.Errors|RMSE",
  escape = FALSE    
)
```

## Table C2: Association between distrust in mail-in ballots and perceptions of election administration (interaction model: by ideology)

```{r}
# Estimate models
lm_count_accurat_mail_ideol <- lm_robust(data = cemp, weights = wt_region_post,
   formula = formula(paste0("count_accurat_5point ~ distrust_mail_vote*ideol + ", controls_mail)), se_type = "HC2")
lm_trust_results_mail_ideol <- lm_robust(data = cemp, weights = wt_region_post,
   formula = formula(paste0("trust_results_5point ~ distrust_mail_vote*ideol + ", controls_mail)), se_type = "HC2")
lm_fair_election_mail_ideol <- lm_robust(data = cemp, weights = wt_region_post,
   formula = formula(paste0("fair_election_4point ~ distrust_mail_vote*ideol + ", controls_mail)), se_type = "HC2")

# Create table
# Create additional row for Fixed Effects
new_rows_C2 <- tribble(~term, ~lm_fair_election_mail_ideol, ~lm_trust_results_mail_ideol, ~lm_count_accurat_mail_ideol,
  "Region fixed effects", "Yes", "Yes", "Yes")
attr(new_rows_C2, 'position') <- c(11)

modelsummary(
  list(
    "Fairness<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_fair_election_mail_ideol,
    "Trust results<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_trust_results_mail_ideol,
    "Votes counted accurately<br>Estimate&nbsp;&nbsp;&nbsp;SE&nbsp;&nbsp;&nbsp;p-value" = lm_count_accurat_mail_ideol
  ),
  coef_map = c(
    "distrust_mail_vote" = "Distrust mail voting",
    "ideol" = "Ideology (L-R)",
    "distrust_mail_vote:ideol" = "Distrust mail voting x Ideology",
    "freq_news_pre" = "Freq. news", 
    "feeling_lib01" = "Liberal Party feeling",
    "interest" = "Political interest",
    "age_cat_pre" = "Age",
    "educ_cat3_pre" = "Education",
    "female_pre" = "Female",
    "(Intercept)" = "Intercept"
  ),
  estimate = "{estimate}&nbsp;&nbsp;{std.error}&nbsp;&nbsp;{p.value}",
  statistic = NULL,
  output = "./Tables/Table_C2.html",
  add_rows = new_rows_C2,
  gof_omit = "AIC|BIC|Std.Errors|RMSE",
  escape = FALSE    
)
```

## Table C3: Do trust in mail-in ballots and trust in elections load onto the same factor? (pre-election)

```{r}
# Using pre-election measure of trust in mail voting
fa_pre <- cemp %>% 
  select(fair_election_4point, trust_results_5point, count_accurat_5point, trust_mail_vote) %>% 
  fa(nfactors = 1)

Statement = c("Thinking about this election, would you say that Elections Canada ran the election… Not at all fairly, Not very fairly, Somewhat fairly, Very fairly",
              "I trust the results of the 2021  Canadian federal election.",
              "I have confidence that the election was administered fairly and the votes were counted accurately.",
              "Voting by mail is equally as trustworthy as voting in person."
)

# Create a table
fa_table_pre <- data.frame(
  Statement = Statement,
  Loading = round(fa_pre$loadings[, 1], 2),
  Communality = round(fa_pre$communality, 2),
  Uniqueness = round(fa_pre$uniqueness, 2)
)

rownames(fa_table_pre) <- NULL

fa_pre$EPVAL_v2 <- ifelse(fa_pre$EPVAL < 0.001, "p < 0.001", paste0("p = ", fa_pre$EPVAL)) 

footnote_pre <- paste0("Empirical chi-square for 1-factor model: ", round(fa_pre$chi, 2), 
                       ", ",
                       fa_pre$EPVAL_v2,
                       ", suggests that 1 factor is not sufficient")

# Save table
fa_table_pre %>%
  kbl(format = "html", 
      digits = c(0, 2, 2, 2)) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) %>%
  add_footnote(footnote_pre) %>% 
  save_kable(file = "./Tables/Table_C3_fa_pre.html")
```

## Table C4: Do trust in mail-in ballots and trust in elections load onto the same factor? (post-election)

```{r}
# Using post-election measure of trust in mail voting
fa_post <- cemp %>% 
  select(fair_election_4point, trust_results_5point, count_accurat_5point, distrust_mail_vote_post) %>% 
  fa(nfactors = 1)

# Create a table
fa_table_post <- data.frame(
  Statement = Statement,
  Loading = round(fa_post$loadings[, 1], 2),
  Communality = round(fa_post$communality, 2),
  Uniqueness = round(fa_post$uniqueness, 2)
)

rownames(fa_table_post) <- NULL

fa_post$EPVAL_v2 <- ifelse(fa_post$EPVAL < 0.001, "p < 0.001", paste0("p = ", fa_post$EPVAL)) 

footnote_post <- paste0("Empirical chi-square for 1-factor model: ", round(fa_post$chi, 2), 
                       ", ",
                       fa_post$EPVAL_v2,
                       ", suggests that 1 factor is not sufficient")

# Save table
fa_table_post %>%
  kbl(format = "html", 
      digits = c(0, 2, 2, 2)) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) %>%
  add_footnote(footnote_post) %>% 
  save_kable(file = "./Tables/Table_C4_fa_post.html")
```

## Table C5: Change in trust in mail-in ballots between the pre- and post-election periods

```{r}
# Run the weighted t-tests
tt_all <- wtd.t.test(x = cemp$distrust_mail_vote_post,
                     y = cemp$distrust_mail_vote,
                     weight = cemp$wt_region_pre, mean = TRUE)

tt_con <- wtd.t.test(x = cemp$distrust_mail_vote_post[cemp$vote_choice=="Conservative"],
                     y = cemp$distrust_mail_vote[cemp$vote_choice=="Conservative"],
                     weight = cemp$wt_region_pre[cemp$vote_choice=="Conservative"], mean = TRUE)

tt_lib <- wtd.t.test(x = cemp$distrust_mail_vote_post[cemp$vote_choice=="Liberal"],
                     y = cemp$distrust_mail_vote[cemp$vote_choice=="Liberal"],
                     weight = cemp$wt_region_pre[cemp$vote_choice=="Liberal"], mean = TRUE)

tt_right <- wtd.t.test(x = cemp$distrust_mail_vote_post[cemp$ideol3 == "Right"],
                       y = cemp$distrust_mail_vote[cemp$ideol3 == "Right"],
                       weight = cemp$wt_region_pre[cemp$ideol3 == "Right"], mean = TRUE)

tt_left <- wtd.t.test(x = cemp$distrust_mail_vote_post[cemp$ideol3 == "Left"],
                      y = cemp$distrust_mail_vote[cemp$ideol3 == "Left"],
                      weight = cemp$wt_region_pre[cemp$ideol3 == "Left"], mean = TRUE)

# Function to extract needed info
extract_ttest_info <- function(tt){
  pre_mean <- tt$additional[3]
  post_mean <- tt$additional[2]
  diff_mean <- post_mean - pre_mean
  t_val <- tt$coefficients[1]
  p_val <- tt$coefficients[3]
  return(c(pre_mean, post_mean, diff_mean, t_val, p_val))
}

# Create a data frame
table_results <- data.frame(
  Group = c("All", "Conservative supporters", "Liberal supporters", "Right-wing ideology", "Left-wing ideology"),
  rbind(
    extract_ttest_info(tt_all),
    extract_ttest_info(tt_con),
    extract_ttest_info(tt_lib),
    extract_ttest_info(tt_right),
    extract_ttest_info(tt_left)
  )
)

# Change column names
colnames(table_results) <- c("Group", "Pre", "Post", "Diff", "t", "p-value")

# Save table
table_results %>%
  kbl(format = "html", 
      digits = c(0, 3, 3, 3, 3, 3)) %>%
  kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover")) %>%
  save_kable(file = "./Tables/Table_C5_t_trust_mail.html")
```
